﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 经典推箱子
{
    /// <summary>
    /// 关卡
    /// </summary>
    public class Level
    {
        /// <summary>
        /// 地图由各种方块在不同位置构成
        /// </summary>
        public List<Block> Map { set; get; }

        /// <summary>
        /// 初始局面
        /// </summary>
        public List<Block> First { set; get; }

        /// <summary>
        /// 历史步骤
        /// 方便撤销
        /// </summary>
        public List<TurnType> History { set; get; }

        /// <summary>
        /// 是否完成本关游戏
        /// 判断算法，当不存在箱子时，视为游戏胜利
        /// </summary>
        /// <returns></returns>
        public bool Success()
        {
            foreach (var block in Map)
            {
                if (block.Type == BlockType.箱子)
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 选择关卡
        /// 从文本记录中读取
        /// 私自约定文件后缀名为level，文件名为关卡序号最小为1，路径为"Level/序号.level"
        /// 文本内容格式为
        /// x,y,t
        /// 分别表示方块的横坐标，纵坐标，类型
        /// </summary>
        /// <param name="no"></param>
        public void Init(int no)
        {
            try
            {
                string[] lines = File.ReadAllLines($"Level/{no}.level");
                this.Map = new List<Block>();
                foreach (var line in lines)
                {
                    string[] numbers = line.Split(',');
                    int x = int.Parse(numbers[0]);
                    int y = int.Parse(numbers[1]);
                    int t = int.Parse(numbers[2]);
                    this.Map.Add(new Block()
                    {
                        X = x,
                        Y = y,
                        Type = (BlockType)t
                    });
                }
                this.First = new List<Block>();
                foreach (var item in this.Map)
                {
                    this.First.Add(item.Clone());
                }
                this.History = new List<TurnType>();
            }
            catch
            {
                throw new Exception($"不存在序号为{no}的关卡");
            }
        }

        /// <summary>
        /// 按指定方向移动主人公
        /// </summary>
        /// <param name="turnType"></param>
        /// <returns></returns>
        public bool Turn(TurnType turnType)
        {
            bool flag = true;

            //边界
            int minX = this.Map.Min(item => item.X);
            int maxX = this.Map.Max(item => item.X);
            int minY = this.Map.Min(item => item.Y);
            int maxY = this.Map.Max(item => item.Y);

            int m = maxX - minX + 1;
            int n = maxY - minY + 1;
            int[,] map = new int[m, n];
            for (int i = 0; i < m; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    map[i, j] = -1;
                }
            }

            //获取主角位置
            int x = -1, y = -1;

            foreach (var item in this.Map)
            {
                map[item.X, item.Y] = (int)item.Type;
                if (item.Type == BlockType.主角 || item.Type == BlockType.进标)
                {
                    x = item.X;
                    y = item.Y;
                }
            }

            switch (turnType)
            {
                case TurnType.上:
                    #region 上面是箱子或完成，若再上面是空地或目标，则可以走，否则不行
                    if (map[x - 1, y] == (int)BlockType.箱子 || map[x - 1, y] == (int)BlockType.完成)
                    {
                        if (map[x - 2, y] == (int)BlockType.空地 || map[x - 2, y] == (int)BlockType.目标)
                        {
                            flag = true;
                            map[x - 2, y] += (int)BlockType.箱子;
                            map[x - 1, y] = map[x - 1, y] - (int)BlockType.箱子 + (int)BlockType.主角;
                            map[x, y] -= (int)BlockType.主角;
                        }
                    }
                    #endregion

                    #region 上面是空地或目标，则可以走
                    else if (map[x - 1, y] == (int)BlockType.空地 || map[x - 1, y] == (int)BlockType.目标)
                    {
                        flag = true;
                        map[x - 1, y] += (int)BlockType.主角;
                        map[x, y] -= (int)BlockType.主角;
                    }
                    #endregion

                    else
                    {
                        flag = false;
                    }
                    break;
                case TurnType.下:
                    #region 下面是箱子或完成，若再下面是空地或目标，则可以走，否则不行
                    if (map[x + 1, y] == (int)BlockType.箱子 || map[x + 1, y] == (int)BlockType.完成)
                    {
                        if (map[x + 2, y] == (int)BlockType.空地 || map[x + 2, y] == (int)BlockType.目标)
                        {
                            map[x + 2, y] += (int)BlockType.箱子;
                            map[x + 1, y] = map[x + 1, y] - (int)BlockType.箱子 + (int)BlockType.主角;
                            map[x, y] -= (int)BlockType.主角;
                        }
                    }
                    #endregion

                    #region 下面是空地或目标，则可以走
                    else if (map[x + 1, y] == (int)BlockType.空地 || map[x + 1, y] == (int)BlockType.目标)
                    {
                        map[x + 1, y] += (int)BlockType.主角;
                        map[x, y] -= (int)BlockType.主角;
                    }
                    #endregion

                    else
                    {
                        flag = false;
                    }

                    break;
                case TurnType.左:
                    #region 左边是箱子或完成，若再左边是空地或目标，则可以走，否则不行
                    if (map[x, y - 1] == (int)BlockType.箱子 || map[x, y - 1] == (int)BlockType.完成)
                    {
                        if (map[x, y - 2] == (int)BlockType.空地 || map[x, y - 2] == (int)BlockType.目标)
                        {
                            map[x, y - 2] += (int)BlockType.箱子;
                            map[x, y - 1] = map[x, y - 1] - (int)BlockType.箱子 + (int)BlockType.主角;
                            map[x, y] -= (int)BlockType.主角;
                        }
                    }
                    #endregion

                    #region 左边是空地或目标，则可以走
                    else if (map[x, y - 1] == (int)BlockType.空地 || map[x, y - 1] == (int)BlockType.目标)
                    {
                        map[x, y - 1] += (int)BlockType.主角;
                        map[x, y] -= (int)BlockType.主角;
                    }
                    #endregion

                    else
                    {
                        flag = false;
                    }

                    break;
                case TurnType.右:
                    #region 右边是箱子或完成，若再右边是空地或目标，则可以走，否则不行
                    if (map[x, y + 1] == (int)BlockType.箱子 || map[x, y + 1] == (int)BlockType.完成)
                    {
                        if (map[x, y + 2] == (int)BlockType.空地 || map[x, y + 2] == (int)BlockType.目标)
                        {
                            map[x, y + 2] += (int)BlockType.箱子;
                            map[x, y + 1] = map[x, y + 1] - (int)BlockType.箱子 + (int)BlockType.主角;
                            map[x, y] -= (int)BlockType.主角;
                        }
                    }
                    #endregion

                    #region 右边是空地或目标，则可以走
                    else if (map[x, y + 1] == (int)BlockType.空地 || map[x, y + 1] == (int)BlockType.目标)
                    {
                        map[x, y + 1] += (int)BlockType.主角;
                        map[x, y] -= (int)BlockType.主角;
                    }
                    #endregion

                    else
                    {
                        flag = false;
                    }
                    break;
            }

            //走成功了，则记录本次移动
            if (flag)
            {
                this.History.Add(turnType);

                this.Map = new List<Block>();
                for (int i = 0; i < m; i++)
                {
                    for (int j = 0; j < n; j++)
                    {
                        if (map[i, j] != -1)
                        {
                            this.Map.Add(new Block()
                            {
                                X = i,
                                Y = j,
                                Type = (BlockType)map[i, j]
                            });
                        }
                    }
                }
            }

            return flag;
        }

        /// <summary>
        /// 撤销
        /// </summary>
        public bool Last()
        {
            try
            {
                if (this.History.Count == 0)
                {
                    return false;
                }
                this.History.RemoveAt(this.History.Count - 1);
                this.Map = new List<Block>();
                foreach (var item in this.First)
                {
                    this.Map.Add(item.Clone());
                }
                List<TurnType> turnTypes = new List<TurnType>();
                foreach(var item in this.History)
                {
                    turnTypes.Add(item);
                }
                this.History.Clear();
                foreach(var item in turnTypes)
                {
                    this.Turn(item);
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
    }

    /// <summary>
    /// 方块类
    /// </summary>
    public class Block
    {
        /// <summary>
        /// 横坐标
        /// </summary>
        public int X { set; get; }
        /// <summary>
        /// 纵坐标
        /// </summary>
        public int Y { set; get; }
        /// <summary>
        /// 方块类型
        /// </summary>
        public BlockType Type { set; get; }

        public Block Clone()
        {
            return (Block)base.MemberwiseClone();
        }
    }

    /// <summary>
    /// 方块类型
    /// </summary>
    public enum BlockType
    {
        /// <summary>
        /// 地图内部空地
        /// </summary>
        空地 = 0,
        /// <summary>
        /// 地图周边和内部墙壁
        /// </summary>
        墙壁 = 1,
        /// <summary>
        /// 箱子
        /// </summary>
        箱子 = 2,
        /// <summary>
        /// 箱子要到达的地方
        /// </summary>
        目标 = 3,
        /// <summary>
        /// 箱子已经进入目标
        /// </summary>
        完成 = 5,
        /// <summary>
        /// 操作的主人公
        /// </summary>
        主角 = 6,
        /// <summary>
        /// 主人公走进目标
        /// </summary>
        进标 = 9
    }

    /// <summary>
    /// 移动方向
    /// </summary>
    public enum TurnType
    {
        上,
        下,
        左,
        右
    }
}
